package org.hilo.boot.core.web;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hilo.boot.core.UT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.filter.OrderedFilter;
import org.springframework.core.Ordered;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 集成请求日志，body重复读取等功能
 * 
 * @author zollty
 * @since 2020年11月18日
 * @see org.springframework.web.filter.AbstractRequestLoggingFilter
 */
public class HighestOrderWebFilter extends OncePerRequestFilter implements OrderedFilter {
    public static final String REQ_LOG_CONTEXT_ID = "reqLogID";
    private int order = Ordered.HIGHEST_PRECEDENCE;
    private boolean traceRequest;
    private final Logger reqLog = LoggerFactory.getLogger("reqLog");

    @Override
    public int getOrder() {
        return this.order;
    }

    /**
     * Set the order for this filter.
     * @param order the order to set
     */
    public void setOrder(int order) {
        this.order = order;
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        
        boolean isFirstRequest = !isAsyncDispatch(request);
        HttpServletRequest requestToUse = request;
        if (isFirstRequest) {
            if (!(request instanceof HiloHttpServletRequestWrapper)) {
                requestToUse = new HiloHttpServletRequestWrapper((HttpServletRequest) request);
            }
        }

        boolean shouldLog = shouldLog(requestToUse);
        if (shouldLog && isFirstRequest) {
            beforeRequest(requestToUse);
        }

        try {
            filterChain.doFilter(requestToUse, response);
        } finally {
            if (shouldLog && !isAsyncStarted(requestToUse)) {
                afterRequest(requestToUse);
            }
        }
        
    }

    public boolean isTraceRequest() {
        return traceRequest;
    }

    public void setTraceRequest(boolean traceRequest) {
        this.traceRequest = traceRequest;
    }
    
    protected boolean shouldLog(HttpServletRequest request) {
        return traceRequest && logger.isDebugEnabled();
    }
    
    /**
     * Writes a log message before the request is processed.
     */
    protected void beforeRequest(HttpServletRequest request) {
        String requestId = UT.Date.getUniqueDatePatternTimeMillisNoSplit();
        request.setAttribute(REQ_LOG_CONTEXT_ID, requestId);
        reqLog.debug(HttpServletRequestHelper.getRequestInfo(request, false, true, false, requestId));
    }

    /**
     * Writes a log message after the request is processed.
     */
    protected void afterRequest(HttpServletRequest request) {
    }

}
